package com.lwc.cfdns.filter;

import com.lwc.cfdns.config.JwtConfig;
import com.lwc.cfdns.constants.Constants;
import com.lwc.cfdns.constants.ConstantsUrl;
import com.lwc.cfdns.exception.AuthException;
import com.lwc.cfdns.pojo.entity.SystemAdmin;
import com.lwc.cfdns.pojo.security.SecuritySystemAdminUserDetails;
import com.lwc.cfdns.utils.JwtUtils;
import com.lwc.cfdns.utils.RedisUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * spring security后台授权认证
 *
 * @author luwc
 * @title JwtAuthenticationTokenFilter
 * @description
 * @time 2023/5/15 13:43:27
 * @version: 1.0
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private JwtConfig jwtConfig;

    /**
     * Security 根据token获取授权及权限
     * 1. 获取token
     * 2. 解析token
     * 3. 从redis获取用户信息对象
     * 4. 存入spring 存入spring SecurityContextHolder
     *
     * @param request
     * @param response
     * @param filterChain
     * @return void
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/5/15 22:33:20
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String uri = request.getRequestURI();
        // 1. 获取token
        String token = request.getHeader(JwtConfig.getHeader());
        // 只有后台接口需要在这里处理
        if (uri.contains(ConstantsUrl.ADMIN) && token != null && StringUtils.hasText(token)) {
            // 2. 解析token
            String sub = null;
            try {
                sub = JwtUtils.validateToken(token);
                // 3. 从redis获取用户信息
                if (StringUtils.hasText(sub) && redisUtils.hasKey(Constants.AUTH_ADMIN_KEY + sub)) {
                    SecuritySystemAdminUserDetails securitySystemAdminUserDetails = (SecuritySystemAdminUserDetails) redisUtils.get(Constants.AUTH_ADMIN_KEY + sub);
                    SystemAdmin admin = securitySystemAdminUserDetails.getSystemAdmin();
                    if (Objects.isNull(admin)) {
                        throw new AuthException("用户未登录或token失效!");
                    }
                    if (JwtUtils.isNeedUpdate(token)) {
                        token = JwtUtils.createToken(sub);
                        response.setHeader(JwtConfig.getHeader(), token);
                        redisUtils.del(Constants.AUTH_ADMIN_KEY + sub);
                        redisUtils.set(Constants.AUTH_ADMIN_KEY + sub, token, jwtConfig.getExpireTime());
                    }
                    // 4. 存入spring SecurityContextHolder
                    // 获取权限信息
                    List<String> permissions = securitySystemAdminUserDetails.getPermissions();
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(admin, null, securitySystemAdminUserDetails.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                    // token过期处理
                    if (JwtUtils.isNeedUpdate(token)) {
                        token = JwtUtils.createToken(sub);
                        // 前后端分离，发起axios请求，无法获取response header的内容 https://blog.csdn.net/qq_42682745/article/details/120640971
                        response.addHeader("Access-Control-Expose-Headers", JwtConfig.getHeader());
                        response.addHeader(JwtConfig.getHeader(), token);
                        redisUtils.del(Constants.AUTH_ADMIN_KEY + sub);
                        redisUtils.set(Constants.AUTH_ADMIN_KEY + sub, securitySystemAdminUserDetails, jwtConfig.getExpireTime());
                    }
                } else {
                    throw new AuthException("用户未登录或token失效!");
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println(e);
                throw new AuthException(e.getMessage());
            }

        }
        // 放行
        filterChain.doFilter(request, response);
    }
}
